package sk.lieskove301.jianghongtiao.liaad.prisoner;

import java.util.Iterator;
import sk.lieskove301.jianghongtiao.liaad.prisoner.strategy.Strategy;
import sk.lieskove301.jianghongtiao.liaad.prisoner.agent.Agent;
import java.util.LinkedList;
import java.util.List;
import sk.lieskove301.jianghongtiao.liaad.prisoner.statistics.charts.PieAgentsChart;
import sk.lieskove301.jianghongtiao.liaad.prisoner.statistics.StatisticStore;
import sk.lieskove301.jianghongtiao.liaad.prisoner.strategy.FightResultEnum;

/**
 * 
 * Place where agents fight and which is responsible for applying strategy and
 * generating agents. 
 * Date of create: May 15, 2011
 *
 * @author JiangHongTiao <jjurco.sk_gmail.com>
 * @version 2011.0515
 */
public class Arena {
    
    private List<Agent> agents = new LinkedList<Agent>();
    private Strategy strategy;
    private int numberOfAgents;
    private StatisticStore store = new StatisticStore();
    
    /**
     * transform result of fight between two agents to Enumeration type
     * @param me result of fight for the first agent
     * @param oposit result of fight for the second agent
     * @return fight result of enumeration fight
     */
    private FightResultEnum makeFightResult(boolean me, boolean oposit){
        if(me && oposit) return FightResultEnum.BothCooperate;
        if(me && !oposit) return FightResultEnum.CooperateBetray;
        if(!me && oposit) return FightResultEnum.BetrayCooperate;
        if(!me && !oposit) return FightResultEnum.BothBetray;
        throw new IllegalArgumentException("Shit happened!");
    }

    /**
     * make fight between agents. Every agent is fighting with everyone else in 
     * one round
     * @param round number of round of the fight
     */
    private void agentsFight(int round){
        List<Agent> played = new LinkedList<Agent>();
        for (Iterator<Agent> it = agents.iterator(); it.hasNext();) {
            Agent ag1 = it.next();
            for (Iterator<Agent> it1 = agents.iterator(); it1.hasNext();) {
                Agent ag2 = it1.next();
                if((ag1 != ag2) && !played.contains(ag2)){
                    boolean ag1C = ag1.cooperate(ag2, round);
                    boolean ag2C = ag2.cooperate(ag1, round);
                    ag1.setFightResults(ag2, makeFightResult(ag1C, ag2C));
                    ag2.setFightResults(ag1, makeFightResult(ag2C, ag1C));
                }
            }
            played.add(ag1);
        }
    }

    /**
     * kill agents based on strategy of killing
     * @param year year of killing
     */
    private void makeGenocide(int year){
        List<Agent> genocide = strategy.genocide(agents);
        for (Iterator<Agent> it = agents.iterator(); it.hasNext();) {
            Agent agent = it.next();
            agent.buryAgents(genocide);
        }
        agents.removeAll(genocide);
        store.putDeadAgents(year, genocide);
    }

    /**
     * renew population of agents based on used strategy
     * @param year year of renewing
     * @return list of new agents generated by strategy
     */
    private List<Agent> renewPopulation(int year){
        List<Agent> renew = strategy.renewPopulation(agents, year);
        agents.addAll(renew);
        for (Iterator<Agent> it = agents.iterator(); it.hasNext();) {
            Agent agent = it.next();
            strategy.agentsAmnesia(agent);
        }
        return renew;
    }

    /**
     * initiate fight between agents
     * @param maxIterations maximal number of years while agents are fighting
     * @param fightsPerIteration number of days of the fight. it means, agents remember 
     * results of fight during this period. After these days genocide happen and 
     * population is renewed. 
     * @return statistics from fight of agents
     */
    public StatisticStore makeFight(int maxIterations, int fightsPerIteration){

        agents = strategy.incubator(numberOfAgents);
        store.putInitialAgents(0, agents);
//        PieAgentsChart pac1 = new PieAgentsChart("Initial", agents, numberOfAgents);
//        pac1.print();
        List<Agent> renew = null;
        for(int i=0; i<maxIterations; i++){
            System.out.println("Round: " + i + " Agents: " + agents.size());
            for(int j=0; j<fightsPerIteration; j++){
                int date = i*maxIterations+j;
                agentsFight(date);
                store.putAllAgents(date, agents);
            }
            store.rememberBest(i, agents);
            makeGenocide(i);
            if(renew != null){
                store.putNewAgents(i, renew);
            }
            renew = renewPopulation(i+1);
        }
        store.putFinalAgents(maxIterations*fightsPerIteration, agents);
//        PieAgentsChart pac = new PieAgentsChart("Agents by gene", agents, numberOfAgents);
//        pac.print();
        return store;
    }

    public List<Agent> getAgents() {
        return agents;
    }

    public void setAgents(List<Agent> agents) {
        this.agents = agents;
    }

    public Strategy getStrategy() {
        return strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public int getNumberOfAgents() {
        return numberOfAgents;
    }

    public void setNumberOfAgents(int numberOfAgents) {
        this.numberOfAgents = numberOfAgents;
    }

    public void setBestN(int bestN) {
        store.setBestN(bestN);
    }

    public int getBestN() {
        return store.getBestN();
    }
    
}
